package com.sun.gis.tools.shapefile;

import org.geotools.data.*;

import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;

import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.locationtech.jts.geom.*;
import org.opengis.feature.simple.SimpleFeature;

import java.io.File;
import java.io.StringWriter;
import java.util.*;

import org.geotools.geojson.geom.GeometryJSON;
import org.opengis.feature.simple.SimpleFeatureType;


/**
 * MultiPolygon 多多边形空洞"缝合"，并打印出GeoJson
 */
public class ShapefileProcessor1 {

    public static void main(String[] args) {
        try {
            File file = new File("/Users/sungang/Desktop/项目/农业保险项目/乌拉特前旗_乔木林数据_示例/test.shp");
            FileDataStore store = FileDataStoreFinder.getDataStore(file);
            SimpleFeatureSource featureSource = store.getFeatureSource();
            SimpleFeatureCollection collection = featureSource.getFeatures();
            try (SimpleFeatureIterator features = collection.features()) {
                while (features.hasNext()) {
                    SimpleFeature feature = features.next();
                    String stitchedPolygonGeoJson = createStitchedPolygonGeoJson(feature);
                    System.out.println(stitchedPolygonGeoJson);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 创建一个新的 SimpleFeature，根据修改后的多边形和特征类型
     *
     * @param polygon 要转换的多边形
     * @param sft     SimpleFeatureType 定义了特征的结构
     * @return 新创建的 SimpleFeature
     */
    private static SimpleFeature createNewSimpleFeature(Polygon polygon, SimpleFeatureType sft) {
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(sft);
        featureBuilder.add(polygon);
        return featureBuilder.buildFeature(null);
    }

    /**
     * 找到并打印多边形内外环之间最近的点对
     *
     * @param feature 要处理的地理特征
     */
    public static void printClosestPointsInFeature(SimpleFeature feature) {
        Geometry geometry = (Geometry) feature.getDefaultGeometry();
        if (geometry instanceof MultiPolygon) {
            MultiPolygon multiPolygon = (MultiPolygon) geometry;
            for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
                Polygon polygon = (Polygon) multiPolygon.getGeometryN(i);
                printClosestPointsInPolygon(polygon);
            }
        }
    }


    /**
     * 找到并打印指定多边形内外环之间最近的点对
     *
     * @param polygon 要处理的多边形
     */
    private static void printClosestPointsInPolygon(Polygon polygon) {
        LinearRing shell = polygon.getExteriorRing();
        for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
            LinearRing hole = polygon.getInteriorRingN(i);
            Coordinate[] closestPoints = findClosestPoints(shell, hole);
            System.out.println("最近的点对: " + closestPoints[0] + ", " + closestPoints[1]);
        }
    }

    /**
     * 找到两个线性环之间最近的点对
     *
     * @param ring1 第一个线性环
     * @param ring2 第二个线性环
     * @return 最近的点对
     */
    private static Coordinate[] findClosestPoints(LinearRing ring1, LinearRing ring2) {
        double minDistance = Double.MAX_VALUE;
        Coordinate closestPoint1 = null;
        Coordinate closestPoint2 = null;

        for (Coordinate coord1 : ring1.getCoordinates()) {
            for (Coordinate coord2 : ring2.getCoordinates()) {
                double distance = coord1.distance(coord2);
                if (distance < minDistance) {
                    minDistance = distance;
                    closestPoint1 = coord1;
                    closestPoint2 = coord2;
                }
            }
        }

        return new Coordinate[]{closestPoint1, closestPoint2};
    }

    /**
     * 生成一个新的多边形，其中内外环通过最近的点对连接
     *
     * @param feature 要处理的地理特征
     * @return 新的Polygon GeoJSON字符串
     */
    public static String createStitchedPolygonGeoJson(SimpleFeature feature) {
        Geometry geometry = (Geometry) feature.getDefaultGeometry();
        if (geometry instanceof MultiPolygon) {
            MultiPolygon multiPolygon = (MultiPolygon) geometry;
            for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
                Polygon polygon = (Polygon) multiPolygon.getGeometryN(i);
                Polygon stitchedPolygon = stitchPolygon(polygon);
                return polygonToGeoJson(stitchedPolygon);
            }
        }
        return null;
    }


    /**
     * 连接多边形的内外环，并返回新的多边形
     *
     * @param polygon 要处理的多边形
     * @return 新的多边形
     */
    private static Polygon stitchPolygon(Polygon polygon) {
        if (polygon.getNumInteriorRing() > 0) {
            LinearRing shell = polygon.getExteriorRing();
            LinearRing hole = polygon.getInteriorRingN(0); // 假设只有一个内环

            Coordinate[] closestPoints = findClosestPoints(shell, hole);

            Coordinate[] newShellCoordinates = createStitchedRingCoordinates(shell, hole, closestPoints[0], closestPoints[1]);
            LinearRing newShell = new GeometryFactory().createLinearRing(newShellCoordinates);

            return new GeometryFactory().createPolygon(newShell);
        } else {
            return polygon; // 没有内环时，直接返回原多边形
        }
    }

    /**
     * 创建连接内外环的新环坐标
     *
     * @param shell             外环
     * @param hole              内环
     * @param shellClosestPoint 外环上最近的点
     * @param holeClosestPoint  内环上最近的点
     * @return 新的环坐标
     */
    private static Coordinate[] createStitchedRingCoordinates(LinearRing shell, LinearRing hole, Coordinate shellClosestPoint, Coordinate holeClosestPoint) {
        List<Coordinate> newCoords = new ArrayList<>();

        // 添加外环坐标，从最近点开始，逆时针方向
        boolean started = false;
        Coordinate[] shellCoords = shell.getCoordinates();
        int shellStartIndex = findCoordinateIndex(shellCoords, shellClosestPoint);

        // 从外环最近点开始添加，逆时针方向
        for (int i = shellStartIndex; i < shellCoords.length; i++) {
            newCoords.add(shellCoords[i]);
        }
        for (int i = 0; i <= shellStartIndex; i++) {
            newCoords.add(shellCoords[i]);
        }

        // 添加从外环到内环的连接点
        newCoords.add(holeClosestPoint);

        // 添加内环坐标，逆时针方向
        Coordinate[] holeCoords = hole.getCoordinates();
        int holeStartIndex = findCoordinateIndex(holeCoords, holeClosestPoint);

        // 从内环最近点开始添加，逆时针方向
        for (int i = holeStartIndex; i < holeCoords.length; i++) {
            newCoords.add(holeCoords[i]);
        }
        for (int i = 0; i <= holeStartIndex; i++) {
            newCoords.add(holeCoords[i]);
        }

        // 再次添加从内环到外环的连接点
        newCoords.add(shellClosestPoint);

        // 确保闭合
        if (!newCoords.get(0).equals2D(newCoords.get(newCoords.size() - 1))) {
            newCoords.add(newCoords.get(0));
        }

        return newCoords.toArray(new Coordinate[0]);
    }

    private static int findCoordinateIndex(Coordinate[] coordinates, Coordinate target) {
        for (int i = 0; i < coordinates.length; i++) {
            if (coordinates[i].equals2D(target)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 将Polygon转换为GeoJSON格式
     *
     * @param polygon 要转换的多边形
     * @return GeoJSON字符串
     */
    private static String polygonToGeoJson(Polygon polygon) {
        StringWriter writer = new StringWriter();
        GeometryJSON godson = new GeometryJSON();
        try {
            godson.write(polygon, writer);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return writer.toString();
    }


}



